﻿#region Imported Libraries
using System.IO;
using System.Text;
using System;
using System.Collections.Generic;
using System.Linq;
#endregion
namespace org.webguvenligi.NSecureImageLib.GraphicFileFormats
{
    public class ExtendedBinaryReader : BinaryReader
    {
        public ExtendedBinaryReader(Stream input)
            : base(input)
        {
        }

        public ExtendedBinaryReader(Stream input, Encoding encoding)
            : base(input, encoding)
        {
        }

        public double ReadFloatingPoint()
        {
            string numberToBeParsed = this.ReadToNull();
            int ePosition = Math.Max(numberToBeParsed.IndexOf('e'),
                             numberToBeParsed.IndexOf('E'));
            string targetParsingNumber = numberToBeParsed.Substring(0, (ePosition < 0 ? numberToBeParsed.Length : ePosition));
            double doublePart = Double.Parse(targetParsingNumber);
            if (ePosition >= 0)
                doublePart *= Math.Pow(10d, Double.Parse(numberToBeParsed.Substring(ePosition + 1)));
            return doublePart;
        }

        public String ReadToNull()
        {
            return ReadToNull(this.BytesRemaining());
        }
        public byte[] ReadBytes()
        {
            List<byte> remainingBytes = new List<byte>();
            while (Int32.MaxValue < this.BytesRemaining())
            {
                remainingBytes.AddRange(this.ReadBytes(Int32.MaxValue));
            }
            if (this.BytesRemaining() > 0)
                remainingBytes.AddRange(this.ReadBytes(Convert.ToInt32(this.BytesRemaining())));
            return remainingBytes.ToArray();
        }

        public String ReadToNull(long maxLength)
        {
            StringBuilder resultBuilder =
                new StringBuilder();
            byte currentByte;
            while (((currentByte = this.ReadByte()) != 0)
                   && (resultBuilder.Length <= maxLength))
            {
                resultBuilder.Append((char)currentByte);
            }

            if (currentByte != 0)
                throw new PNG.Exceptions.PNGBaseException();

            return resultBuilder.ToString();

        }

        /// <summary>
        /// Checks The Remaining Bytes To Be Read In The Stream
        /// </summary>
        /// <param name="sourceStream">The Stream To Be Checked</param>
        /// <returns>The Remaining Bytes In Stream</returns>
        public long BytesRemaining()
        {
            return (this.BaseStream.Length - this.BaseStream.Position);
        }



        public long ReadLittleEndianInt64()
        {
            byte[] bytes = new byte[sizeof(long)];
            for (int i = 0; i < sizeof(long); i += 1)
            {
                bytes[sizeof(short) - 1 - i] = this.ReadByte();
            }
            return BitConverter.ToInt64(bytes, 0);
        }

        public short ReadLittleEndianInt16()
        {
            byte[] bytes = new byte[sizeof(short)];
            for (int i = 0; i < sizeof(short); i += 1)
            {
                bytes[sizeof(short) - 1 - i] = this.ReadByte();
            }
            return BitConverter.ToInt16(bytes, 0);
        }

        public ushort ReadLittleEndianUInt16()
        {
            byte[] bytes = new byte[sizeof(short)];
            for (int i = 0; i < sizeof(short); i += 1)
            {
                bytes[sizeof(short) - 1 - i] = this.ReadByte();
            }
            return BitConverter.ToUInt16(bytes, 0);
        }

        public uint ReadLittleEndianUInt32()
        {
            byte[] bytes = new byte[sizeof(int)];
            for (int i = 0; i < sizeof(int); i += 1)
            {
                bytes[sizeof(int) - 1 - i] = this.ReadByte();
            }
            return BitConverter.ToUInt32(bytes, 0);
        }

        public int ReadLittleEndianInt32()
        {
            byte[] bytes = new byte[sizeof(int)];
            for (int i = 0; i < sizeof(int); i += 1)
            {
                bytes[sizeof(int) - 1 - i] = this.ReadByte();
            }
            return BitConverter.ToInt32(bytes, 0);
        }
        /*
        public void WriteLittleEndian(uint value)
        {
            this.Write(BitConverter.GetBytes(value).Reverse().ToArray());
        }
        public void WriteLittleEndian(int value)
        {
            this.Write(BitConverter.GetBytes(value).Reverse().ToArray());
        }
        public void WriteLittleEndian(short value)
        {
            this.Write(BitConverter.GetBytes(value).Reverse().ToArray());
        }
        public void WriteLittleEndian(ushort value)
        {
            this.Write(BitConverter.GetBytes(value).Reverse().ToArray());
        }
        public void WriteLittleEndian(long value)
        {
            this.Write(BitConverter.GetBytes(value).Reverse().ToArray());
        }
        public void WriteLittleEndian(ulong value)
        {
            this.Write(BitConverter.GetBytes(value).Reverse().ToArray());
        }
        */

        /// <summary>
        /// Ensures That The Given BinaryReader Contains Enough Bytes To Be Readed
        /// </summary>
        /// <param name="sourceReader">The BinaryReader To Be Checked</param>
        /// <param name="byteCount">The Total Number Of Byte That Must Be Remaining In The Current Stream</param>
        /// <exception cref="System.IO.InvalidDataException"></exception>
        public void EnsureBytes(long byteCount)
        {
            if (!(BytesRemaining() >= byteCount))
                throw new InvalidDataException("Invalid Stream Length");
        }

        public byte[] ReadBytesEnsure(int byteCount)
        {
            EnsureBytes(byteCount);
            return this.ReadBytes(byteCount);
        }

        public void CheckTotalLength(long byteCount)
        {
            if (BytesRemaining() != byteCount)
                throw new InvalidDataException("Invalid Stream Length");
        }
    }
}